From 25abb5ab1ac8a6c5dae38740d9f7bf804c953d78 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 11 Jul 2014 08:06:39 -0700 Subject: [PATCH] Refactor `Job` to its own module --- src/cargo/ops/cargo_rustc/job.rs | 57 +++++++++++++ .../{cargo_rustc.rs => cargo_rustc/mod.rs} | 84 +++++-------------- 2 files changed, 78 insertions(+), 63 deletions(-) create mode 100644 src/cargo/ops/cargo_rustc/job.rs rename src/cargo/ops/{cargo_rustc.rs => cargo_rustc/mod.rs} (88%) diff --git a/src/cargo/ops/cargo_rustc/job.rs b/src/cargo/ops/cargo_rustc/job.rs new file mode 100644 index 000000000..613ad0dd1 --- /dev/null +++ b/src/cargo/ops/cargo_rustc/job.rs @@ -0,0 +1,57 @@ +use util::CargoResult; +use std::sync::{Arc, Mutex}; + +pub struct Job { + work: proc():Send -> CargoResult>, +} + +impl Job { + /// Create a new job representing a unit of work. + pub fn new(work: proc():Send -> CargoResult>) -> Job { + Job { work: work } + } + + /// Creates a new job which will execute all of `jobs` and then return the + /// work `after` if they all succeed sequentially. + pub fn all(jobs: Vec, after: Vec) -> Job { + Job::new(proc() { + for job in jobs.move_iter() { + try!(job.run()); + } + Ok(after) + }) + } + + /// Maps a list of jobs to a new list of jobs which will run `after` once + /// all the jobs have completed. + pub fn after(jobs: Vec, after: Job) -> Vec { + if jobs.len() == 0 { return vec![after] } + + struct State { job: Option, remaining: uint } + + let lock = Arc::new(Mutex::new(State { + job: Some(after), + remaining: jobs.len(), + })); + + jobs.move_iter().map(|job| { + let my_lock = lock.clone(); + Job::new(proc() { + try!(job.run()); + let mut state = my_lock.lock(); + state.remaining -= 1; + Ok(if state.remaining == 0 { + vec![state.job.take().unwrap()] + } else { + Vec::new() + }) + }) + }).collect() + } + + /// Consumes this job by running it, returning the result of the + /// computation. + pub fn run(self) -> CargoResult> { + (self.work)() + } +} diff --git a/src/cargo/ops/cargo_rustc.rs b/src/cargo/ops/cargo_rustc/mod.rs similarity index 88% rename from src/cargo/ops/cargo_rustc.rs rename to src/cargo/ops/cargo_rustc/mod.rs index 2ec1626bf..343f22d9d 100644 --- a/src/cargo/ops/cargo_rustc.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -5,7 +5,6 @@ use std::io::{File, IoError}; use std::io; use std::os::args; use std::str; -use std::sync::{atomics, Arc}; use term::color::YELLOW; use core::{Package, PackageSet, Target, Resolve}; @@ -13,6 +12,10 @@ use util; use util::{CargoResult, ChainError, ProcessBuilder, CargoError, internal, human}; use util::{Config, TaskPool, DependencyQueue, Fresh, Dirty, Freshness}; +use self::job::Job; + +mod job; + type Args = Vec; struct Context<'a, 'b> { @@ -26,21 +29,6 @@ struct Context<'a, 'b> { dylib: (String, String) } -enum Job { - Work(proc():Send -> CargoResult>), -} - -impl Job { - fn all(jobs: Vec, after: Vec) -> Job { - Work(proc() { - for Work(job) in jobs.move_iter() { - try!(job()); - } - Ok(after) - }) - } -} - // This is a temporary assert that ensures the consistency of the arguments // given the current limitations of Cargo. The long term fix is to have each // Target know the absolute path to the build location. @@ -146,22 +134,6 @@ fn compile<'a>(targets: &[&Target], pkg: &'a Package, cx: &mut Context, return Ok(()) } - // First check to see if this package is fresh. - // - // Note that we're compiling things in topological order, so if nothing has - // been built up to this point and we're fresh, then we can safely skip - // recompilation. If anything has previously been rebuilt, it may have been - // a dependency of ours, so just go ahead and rebuild ourselves. - // - // This is not quite accurate, we should only trigger forceful - // recompilations for downstream dependencies of ourselves, not everyone - // compiled afterwards.a - let fingerprint_loc = cx.dest.join(format!(".{}.fingerprint", - pkg.get_name())); - - let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx, - targets)); - // First part of the build step of a target is to execute all of the custom // build commands. // @@ -195,34 +167,20 @@ fn compile<'a>(targets: &[&Target], pkg: &'a Package, cx: &mut Context, // TODO: Can a fingerprint be per-target instead of per-package? Doing so // would likely involve altering the granularity of key for the // dependency queue that is later used to run jobs. - let state = Arc::new(atomics::AtomicUint::new(bins.len())); - let write_fingerprint = || { - let (my_state, fingerprint_loc, fingerprint) = - (state.clone(), fingerprint_loc.clone(), fingerprint.clone()); - Work(proc() { - if my_state.load(atomics::SeqCst) == 0 { - let mut file = try!(File::create(&fingerprint_loc)); - try!(file.write_str(fingerprint.as_slice())); - } - Ok(Vec::new()) - }) - }; + let fingerprint_loc = cx.dest.join(format!(".{}.fingerprint", + pkg.get_name())); + + let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx, + targets)); + let write_fingerprint = Job::new(proc() { + try!(File::create(&fingerprint_loc).write_str(fingerprint.as_slice())); + Ok(Vec::new()) + }); // Note that we build the job backwards because each job will produce more // work. - let build_libs = if bins.len() == 0 { - Job::all(libs, vec![write_fingerprint()]) - } else { - Job::all(libs, bins.move_iter().map(|Work(bin)| { - let my_state = state.clone(); - let write = write_fingerprint(); - Work(proc() { - try!(bin()); - my_state.fetch_sub(1, atomics::SeqCst); - Ok(vec![write]) - }) - }).collect()) - }; + let bins = Job::after(bins, write_fingerprint); + let build_libs = Job::all(libs, bins); let job = Job::all(build_cmds, vec![build_libs]); jobs.push((pkg, if is_fresh {Fresh} else {Dirty}, job)); @@ -278,7 +236,7 @@ fn compile_custom(pkg: &Package, cmd: &str, for arg in cmd { p = p.arg(arg); } - Work(proc() { + Job::new(proc() { try!(p.exec_with_output().map(|_| ()).map_err(|e| e.mark_human())); Ok(Vec::new()) }) @@ -301,7 +259,7 @@ fn rustc(package: &Package, target: &Target, cx: &mut Context) -> Job { shell.status("Running", rustc.to_string()) }); - Work(proc() { + Job::new(proc() { if primary { log!(5, "executing primary"); try!(rustc.exec().map_err(|err| human(err.to_string()))) @@ -467,11 +425,11 @@ fn execute(config: &mut Config, try!(config.shell().status("Fresh", pkg)); tx.send((name, Fresh, Ok(Vec::new()))); } - Some((name, Dirty, (pkg, Work(job)))) => { + Some((name, Dirty, (pkg, job))) => { assert!(active.insert(name.clone(), 1)); try!(config.shell().status("Compiling", pkg)); let my_tx = tx.clone(); - pool.execute(proc() my_tx.send((name, Dirty, job()))); + pool.execute(proc() my_tx.send((name, Dirty, job.run()))); } None => break, } @@ -484,12 +442,12 @@ fn execute(config: &mut Config, *active.get_mut(&name) -= 1; match result { Ok(v) => { - for Work(job) in v.move_iter() { + for job in v.move_iter() { *active.get_mut(&name) += 1; let my_tx = tx.clone(); let my_name = name.clone(); pool.execute(proc() { - my_tx.send((my_name, fresh, job())); + my_tx.send((my_name, fresh, job.run())); }); } if *active.get(&name) == 0 { -- 2.30.2